﻿using NServiceKit.Redis;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;

namespace HobSoft.SoccerBet2014.Domain.Implementation.RedisRepository
{
    using Domain.Common.SiteContext;
    using Domain.Models.Site;

    public class CookiesManager : ReadWriteRedisRepository<AccountCookie>, ICookiesManager
    {
        public CookiesManager(PooledRedisClientManager pooledRedisClient)
            : base(pooledRedisClient)
        {

        }

        public CookieContainer GetCookies(byte bookerId, string account, Uri url)
        {
            using (var redis = CreateRedisClient())
            {
                var redisCookie = redis.As<AccountCookie>();

                List<AccountCookie> httpOnlyCookies = new List<AccountCookie>();
                CookieContainer cookies = new CookieContainer();
                var accountCookies = redisCookie.GetAll().Where(c => c.BmID == bookerId && c.Account == account);
                foreach (var ack in accountCookies)
                {
                    if (ack.Cookie.HttpOnly)
                    {
                        ack.Cookie.Expires = DateTime.Now.AddYears(1);
                        ack.LastUpdate = DateTime.Now;
                        httpOnlyCookies.Add(ack);
                    }
                    if (url.Host.EndsWith(ack.Cookie.Domain, StringComparison.OrdinalIgnoreCase))
                        cookies.Add(url, ack.Cookie.ToCookie());
                }
                if (httpOnlyCookies.Count > 0)
                    redisCookie.StoreAll(httpOnlyCookies);
                redisCookie.RedisClient.Dispose();
                return cookies;
            }
        }

        public Cookie GetCookie(byte bookerId, string account, string cookieName, Uri url)
        {
            using (var redis = CreateRedisClient())
            {
                var redisCookie = redis.As<AccountCookie>();

                var accountCookie = redisCookie.GetAll().FirstOrDefault(c => c.BmID == bookerId
                    && c.Account == account
                    && c.Cookie.Name == cookieName
                    && url.Host.EndsWith(c.Cookie.Domain, StringComparison.OrdinalIgnoreCase));
                redisCookie.RedisClient.Dispose();
                if (accountCookie == null)
                {
                    return null;
                }
                if (accountCookie.Cookie.HttpOnly)
                {
                    accountCookie.Cookie.Expires = DateTime.Now.AddYears(1);
                    accountCookie.LastUpdate = DateTime.Now;
                    redisCookie.Store(accountCookie);
                }

                return accountCookie.Cookie.ToCookie();
            }
        }

        public Cookie GetHttpCookie(byte bookerId, string account, string cookieName, DateTime updateTime, IEnumerable<string> exceptDomain)
        {
            using (var redis = CreateRedisClient())
            {
                var redisCookie = redis.As<AccountCookie>();

                var accountCookies = redisCookie.GetAll().Where(c => c.BmID == bookerId
                                                && c.Account == account
                                                && c.Cookie.Name == cookieName
                                                && c.LastUpdate > updateTime
                                                && !exceptDomain.Contains(c.Cookie.Domain)).OrderByDescending(c => c.LastUpdate);
                var ack = accountCookies.Select(c => c.Cookie).FirstOrDefault();
                redisCookie.RedisClient.Dispose();
                if (ack == null) return null;
                return ack.ToCookie();
            }
        }        

        public Cookie GetHttpCookie(byte bookerId, string account, string cookieName, string domain)
        {
            using (var redis = CreateRedisClient())
            {
                using (var redisCookie = redis.As<AccountCookie>())
                {
                    var accountCookies = redisCookie.GetAll().Where(c => c.BmID == bookerId
                                                    && c.Account == account
                                                    && c.Cookie.Name == cookieName
                                                    && c.Cookie.Domain.Equals(domain, StringComparison.OrdinalIgnoreCase));
                    var ack = accountCookies.Select(c => c.Cookie).FirstOrDefault();
                    redisCookie.RedisClient.Dispose();
                    if (ack == null) return null;
                    return ack.ToCookie();
                }
            }
        }

        public void DeleteCookies(byte bookerId, string account, string cookieName, IEnumerable<string> exceptDomain)
        {
            using (var redis = CreateRedisClient())
            {
                using (var redisCookie = redis.As<AccountCookie>())
                {
                    var accountCookies = redisCookie.GetAll().Where(c => c.BmID == bookerId
                                                    && c.Account == account
                                                    && c.Cookie.Name == cookieName);
                    foreach (var ack in accountCookies)
                    {
                        if (!exceptDomain.Contains(ack.Cookie.Domain))
                            redisCookie.DeleteById(ack.Key);
                    }
                    redisCookie.RedisClient.Dispose();
                }
            }
        }

        public void SetCookies(byte bookerId, string account, Cookie cookie)
        {
            using (var redis = CreateRedisClient())
            {
                using (var redisCookie = redis.As<AccountCookie>())
                {
                    var accountCookie = redisCookie.GetAll().FirstOrDefault(c => c.BmID == bookerId
                        && c.Account == account
                        && c.Cookie.Name == cookie.Name
                        && (
                        c.Cookie.Domain.EndsWith(cookie.Domain, StringComparison.OrdinalIgnoreCase)
                        || cookie.Domain.EndsWith(c.Cookie.Domain, StringComparison.OrdinalIgnoreCase)));
                    if (accountCookie == null)
                    {
                        accountCookie = new AccountCookie()
                        {
                            Key = redisCookie.GetNextSequence(),
                            BmID = bookerId,
                            Account = account,
                            LastUpdate = DateTime.Now,
                            Cookie = new PersistentCookie(cookie)
                        };
                    }
                    else
                    {
                        accountCookie.Cookie = new PersistentCookie(cookie);
                    }
                    redisCookie.Store(accountCookie);
                    redisCookie.RedisClient.Dispose();
                }
            }
        }

        public void SetCookies(byte bookerId, string account, CookieCollection cookies)
        {
            if (cookies == null || cookies.Count == 0) return;
            using (var redis = CreateRedisClient())
            {
                using (var redisCookie = redis.As<AccountCookie>())
                {
                    var accountCookies = redisCookie.GetAll().Where(c => c.BmID == bookerId && c.Account == account);
                    var updateCookies = new List<AccountCookie>();
                    foreach (Cookie cookie in cookies)
                    {
                        var old = accountCookies.FirstOrDefault(c => c.Cookie.Name == cookie.Name
                            && (
                            c.Cookie.Domain.EndsWith(cookie.Domain, StringComparison.OrdinalIgnoreCase)
                            || cookie.Domain.EndsWith(c.Cookie.Domain, StringComparison.OrdinalIgnoreCase)
                            ));
                        if (old == null)
                        {
                            AccountCookie ack = new AccountCookie()
                            {
                                Key = redisCookie.GetNextSequence(),
                                BmID = bookerId,
                                Account = account,
                                LastUpdate = DateTime.Now,
                                Cookie = new PersistentCookie(cookie)
                            };
                            updateCookies.Add(ack);
                        }
                        else
                        {
                            old.Cookie = new PersistentCookie(cookie);
                            old.LastUpdate = DateTime.Now;
                            updateCookies.Add(old);
                        }
                    }
                    redisCookie.StoreAll(updateCookies);
                    redisCookie.RedisClient.Dispose();
                }
            }
        }

        public void Clear(byte bookerId, string account)
        {
            using (var redis = CreateRedisClient())
            {
                using (var redisCookie = redis.As<AccountCookie>())
                {
                    var accountCookies = redisCookie.GetAll().Where(c => c.BmID == bookerId && c.Account == account);
                    using (var tran = redisCookie.CreateTransaction())
                    {
                        foreach (var ack in accountCookies)
                        {
                            tran.QueueCommand(rc => rc.DeleteById(ack.Key));
                        }
                        tran.Commit();
                    }
                    redisCookie.RedisClient.Dispose();
                }
            }
        }
    }
}
